<?php

class TemplateContentService extends FServiceBase
{    
    /**
    * Get a TemplateContent model given its ID
    * 
    * @param int id TemplateContent ID
    * @return FServiceModel
    */
    public function get($params){
        $model = TemplateContent::model()->findByPk($this->getParam($params, 'id',0));
        if (! $model)
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.TemplateContent','Invalid ID.'));
        $this->result->processed('model', $model);
        return $this->result;
    }
    
    public function save($params) {
        /**
        * @var CModel
        */
        $model = $this->getModel($params['TemplateContent'],'TemplateContent',array('html'=>'xss,tag,newline'));
        $this->result->processed('model', $model);
        
        if (! $model->validate())
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.TemplateContent', 'Submitted data is missing or invalid.'));
        elseif ($this->getParam($params, 'validateOnly',0) == TRUE)
            return $this->result;
        elseif (! $model->save())
            $this->result->fail(ERROR_HANDLING_DB, Yii::t('Cms.TemplateContent','Error while saving submitted data into database.'));
        
        return $this->result;
    }


    public function delete($params) {
        $ids = $this->getParam($params, 'ids', array());
        if ($ids == 0) {
            return $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.TemplateContent','Invalid ID.'));
        }
      
        if (!is_array($ids)) $ids = array($ids);
        $criteria = new CDbCriteria();
        $criteria->addInCondition('id', $ids);
        TemplateContent::model()->deleteAll($criteria);
        return $this->result;
    }
    
    public function getRevisionByTemplate($params)
    {
    	$template_id = $this->getParam($params, 'template_id', 0);
        $revisions = TemplateContent::model()->findAll("template_id = {$template_id} AND status = true");
        $this->result->processed('revisions', $revisions);
        return $this->result;
    }
    
    public function getContainers($params)
    {
        $pageId = $this->getParam($params, 'page_id', 0);
        $status = $this->getParam($params, 'status', null);
        $criteria = new CDbCriteria();
        $criteria->join = 'INNER JOIN '.SITE_TABLE_PREFIX.'page_revision p ON "p".template_content_id = "t".id';
        $criteria->compare('"p".page_id', $pageId);
        if ($status !== null)
            $criteria->compare('t.status', $status);
        $model = TemplateContent::model()->find($criteria);
        
        $containers = array();
        if (is_object($model))
        {
            if (preg_match_all('/<([^>]*)class="([^"]*)sb\-container([^"]*)"([^>]*)>/im', $model->html, $matches))
            {
                foreach ($matches[0] as $str)
                {
                    if (preg_match('/id="([^"]+)"/im', $str, $m))
                        $containers[] = array(
                            'id'=>$m[1],
                            'name'=>$m[1],
                        );
                }
            }
        }
            
        $this->result->processed('models', $containers);
        return $this->result;
    }
    
    public function getList($params)
    {
        $status = $this->getParam($params, 'status', null);
        $siteId = $this->getParam($params, 'site_id', Yii::app()->cmsManager->site['id']);
        $group = $this->getParam($params, 'group', '');
        $exceptMasterLayout = $this->getParam($params, 'exceptMasterLayout', FALSE);
        
        $criteria = new CDbCriteria();
        $criteria->with = array(
            'template'=>array(
                'joinType'=>'INNER JOIN',
            ),
        );
        $criteria->join = 'INNER JOIN '.SITE_TABLE_PREFIX.'template_site ts ON ts.template_id = t.template_id';
        $criteria->compare('t.status', $status);
        if (!empty($group))
            $criteria->addSearchCondition('template.group',$group);
        $criteria->compare('ts.site_id', $siteId);
        $criteria->order = 'template.name ASC, t.revision_id DESC';
        if ($exceptMasterLayout==TRUE)
            $criteria->addCondition("template.group <> 'master'");
        $models = TemplateContent::model()->findAll($criteria);
        $this->result->processed('models', $models);
        return $this->result;
    }
    
    public function getRevisions($params)
    {
        $templateId = $this->getParam($params, 'template_id', 0);
        
        $revisions = array();
        if (!$templateId)
        {
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.TemplateContent','Invalid template ID.'));
        }
        else
        {
            $criteria = new CDbCriteria();
            $criteria->compare('template_id', $templateId);
            $criteria->order = 'revision_id DESC';
            $items = TemplateContent::model()->findAll($criteria);
            if (count($items))
            {
                foreach ($items as $item)
                {
                    $log = $item->log;
                    if (empty($log) === false)
                    {
                        $log = strip_tags($log);
                        list($log,) = explode("\n", $log, 2);
                        Yii::import('Cms.helpers.DataHelper');
                        $logShort = DataHelper::truncateWords($log, 10);
                        $log = ' - '.(strlen($log) > strlen($logShort) ? $logShort.'...' : $logShort);
                    }
                    $revisions[$item->revision_id] = $item->revision_id.$log;
                }
            }
        }
        
        $this->result->processed('items', $revisions);
        return $this->result;
    }
    
    /**
    * find all containers of template
    * 
    * @param mixed $params
    *   id          int     template_content_id
    *   recursive   boolean find all containers of recursive parents of current template
    *   include_layout   boolean find all containers of recursive parents of current template
    */
    public function findAllContainters($params)
    {
        $id = $this->getParam($params, 'id', 0);
        $recursive = $this->getParam($params, 'recursive', false);
        $includeLayout = $this->getParam($params, 'include_layout', false);
        
        $containers = array('cms-header'=>array());
        $result = $this->get(array('id'=>$id));
        $model = null;
        if (! $result->hasErrors())
        {
            $model = $result->model;
        }
        
        $htmlPurifier = new CHtmlPurifier();
        $htmlPurifier->options = array(
            'Core.RemoveProcessingInstructions'=>true,
            'Attr.EnableID'=>true,
        );
        while (is_object($model))
        {
            //detect container
            $this->detectContainer($htmlPurifier, $containers, $model->html);
            //find containers in recursive template
            $model = $model->parent;
        }
        $containers['cms-footer'] = array();
        
        //find containers auto render by layout manager
        if ($includeLayout)
        {
            foreach ($containers as $container => $val)
            {
                $result = FSM::_run('Cms.LayoutContainer.findLayoutsByContainer', array('container'=>$container));
                if ($result->html !== false)
                {
                    $this->detectContainer($htmlPurifier, $containers, $result->html);
                }
            }
        }
        
        return $this->result->processed('containers', $containers);
    }
    
    /**
    * load containers auto render by Layout Manage
    * 
    * @param CHtmlPurifier $htmlPurifier
    * @param array $containers new containers will assign to $containers variable
    * @param string $text
    * 
    * @return void
    */
    protected function detectContainer($htmlPurifier, &$containers, $text)
    {
        $content = $htmlPurifier->purify($text);
        $content = '<root>'.$content.'</root>';
        if (empty($content) === false)
        {
            libxml_use_internal_errors(true);
            $xml = new SimpleXMLElement($content);
            if (!$xml)
            {
                $errors = libxml_get_errors();
                foreach ($errors as $error)
                {
                    Yii::log($this->formatXmlError($error, explode("\n", $content)), CLogger::LEVEL_ERROR);
                }
                libxml_clear_errors();
            }
            $result = $xml->xpath("//*[contains(@class,'sb-container')]");
            while(list( , $node) = each($result)) {
                $attributes = $node->attributes();
                if (count($attributes) && isset($attributes['id']))
                    $containers[(string) $attributes['id']] = array();
            }
        }
    }
    
    protected function displayXmlError($error, $xml)
    {
        $return  = $xml[$error->line - 1] . "\n";
        $return .= str_repeat('-', $error->column) . "^\n";

        switch ($error->level) {
            case LIBXML_ERR_WARNING:
                $return .= "Warning $error->code: ";
                break;
             case LIBXML_ERR_ERROR:
                $return .= "Error $error->code: ";
                break;
            case LIBXML_ERR_FATAL:
                $return .= "Fatal Error $error->code: ";
                break;
        }

        $return .= trim($error->message) .
                   "\n  Line: $error->line" .
                   "\n  Column: $error->column";

        if ($error->file) {
            $return .= "\n  File: $error->file";
        }

        return "$return\n\n--------------------------------------------\n\n";
    }
    
    public function deleteByTemplateId($params)
    {
        $templateId = $this->getParam($params, 'template_id', 0);
        
        $result = TemplateContent::model()->deleteAllByAttributes(array('template_id'=>$templateId));
        
        return $this->result->processed('status', $result);
    }
    
    /**
    * check template_content_id is used by any pages?
    * 
    * @param array $params
    * id int template_content_id
    * exclude array page_id exclude
    */
    public function isUsed($params)
    {
        //Yii::trace(__CLASS__.'::'.__FUNCTION__, __CLASS__);
        $exclude = $this->getParam($params, 'exclude', array());
        $id = $this->getParam($params, 'id', 0);
        
        $status = false;
        $canDeleteTemplate = false;
        if ($id)
        {
            $status = $this->pagesUsed(array($id), $exclude);
            //if template content not used
            //check can delete template
            if (! $status)
            {
                //find all template revision of current template
                $templateContent = TemplateContent::model()->findByPk($id);
                if (is_object($templateContent))
                {
                    $templateContents = TemplateContent::model()->findAllByAttributes(array('template_id'=>$templateContent->template_id));
                    if (count($templateContents))
                    {
                        $templateContents = CHtml::listData($templateContents, 'id', 'id');
                        $templateContents = array_diff($templateContents, array($id));
                    }
                    if (count($templateContents))
                    {
                        if (! $this->pagesUsed($templateContents, $exclude))
                        {
                            $canDeleteTemplate = $templateContent->template_id;
                        }
                    }
                    else
                    {
                        $canDeleteTemplate = $templateContent->template_id;
                    }
                }
            }
        }
        
        //Yii::trace('$status '.$status, __CLASS__);
        //Yii::trace('$canDeleteTemplate '.$canDeleteTemplate, __CLASS__);
        $this->result->processed('canDeleteTemplate', $canDeleteTemplate);
        return $this->result->processed('status', $status);
    }
    
    protected function pagesUsed($templateContentIds, $exclude)
    {
        //Yii::trace(__CLASS__.'::'.__FUNCTION__, __CLASS__);
        if ((is_array($templateContentIds) && count($templateContentIds)) === false)
        {
            $this->result->fail(ERROR_INVALID_DATA, Yii::t('Cms.TemplateContent','Invalid template IDs.'));
            return false;
        }
        
        //find pages used template
        $criteria = new CDbCriteria();
        $criteria->addInCondition('template_content_id', $templateContentIds);
        $criteria->select = array('page_id');
        $criteria->distinct = true;
        $models = PageRevision::model()->findAll($criteria);
        if (count($models))
        {
            $models = CHtml::listData($models, 'page_id', 'page_id');
            if (count($exclude))
            {
                $models = array_diff($models, $exclude);
            }
            //Yii::trace('$models '.CVarDumper::dumpAsString($models), __CLASS__);
            return count($models);
        }
        return 0;
    }
    
    public function getChildrenTemplate($params)
    {
        $templateId = $this->getParam($params, 'id', 0);
        
        $children = array();
        if ($templateId)
        {
            $children = $this->findChildren($templateId);
        }
        return $this->result->processed('children', $children);
    }
    
    protected function findChildren($parentId)
    {
        static $children;
        if (! isset($children))
            $children = array();
            
        $models = TemplateContent::model()->findAllByAttributes(array('parent_id'=>$parentId));
        if (count($models))
        {
            foreach ($models as $model)
            {
                $children[] = $model->id;
                $this->findChildren($model->id);
            }
        }
        return $children;
    }
    
    /**
    * get all widgets group by container
    * 
    * @param array $params
    *   id int template_content_id
    *   prefix string default 'items_'
    * 
    * @return array data items
    *   array(
    *       'container'=>array(
    *           'items_<widget_id>'=>'<widget_name>'
    *       )
    *   )
    */
    public function getAllWidgets($params)
    {
        $id = $this->getParam($params, 'id', 0);
        $prefix = $this->getParam($params, 'prefix', 'items_');
        
        $criteria = new CDbCriteria;
        $criteria->compare('t.template_content_id', $id);
        $criteria->compare('t.page_id', 0);
        $criteria->compare('t.revision_id', 0);
        $criteria->order = 't.ordering';
        $criteria->with = array('widget');
        $models = PageWidget::model()->findAll($criteria);
        $data=array();
        if (is_array($models) && count($models))
        {
            foreach ($models as $model)
            {
                $params = array();
                if (is_string($model->widget_settings))
                    $params = $this->evaluateExpression($model->widget_settings);
                if (is_array($params))
                {
                    if (isset($params['widget']))
                    {
                        if (!isset($params['widget']['criteria']))
                            $params = $params['widget'];
                    }
                }
                else
                    $params = array();
                $params['page_widget_id'] = $model->id;
                array_unshift($params, '/'.trim($model->widget->path,'/').'/update');
                if (isset($data[$model->container]))
                {
                    $data[$model->container][$prefix.$model->id] = CHtml::link($model->widget->name.'('.$model->id.')',$params, array('class'=>'widget-title')) ;
                }
                else
                {
                    $data[$model->container] = array($prefix.$model->id=>CHtml::link($model->widget->name.'('.$model->id.')',$params, array('class'=>'widget-title')));
                }
            }
        }
        
        $this->result->processed('data', $data);
    }
    
    /**
    * link widgets of template with page
    * 
    * @param array $params
    *   page_id                 int     page id
    *   revision                int     page revision
    *   template_content_id     int     template content id
    *   
    * @return FServiceModel
    * 
    */
    public function linkWidgetsToPage($params)
    {
        $containers = $this->getParam($params, 'containers', array(), 'xss, newline, tag');
        $pageId = $this->getParam($params, 'page_id', 0);
        $revision = $this->getParam($params, 'revision_id', 0);
        $tplContentId = $this->getParam($params, 'template_content_id', 0);
        
        $models = PageWidget::model()->findAllByAttributes(array('template_content_id'=>$tplContentId));
        if (is_array($models) && count($models))
        {
            foreach ($models as $model)
            {
                $wsId = $model->id;
                $criteria = new CDbCriteria;
                $criteria->compare('widget_settings_id',$wsId);
                $criteria->compare('page_id',$pageId);
                $criteria->compare('revision_id',$revision);
                $criteria->compare('container',$model->container);
                if (!PageWidget::model()->exists($criteria))
                {
                    //find ordering
                    $criteria = new CDbCriteria;
                    $criteria->order = 'ordering DESC';
                    $criteria->compare('page_id',$pageId);
                    $criteria->compare('revision_id',$revision);
                    $criteria->compare('container',$model->container);
                    $pageWidget = PageWidget::model()->find($criteria);
                    $ordering = is_object($pageWidget) ? $pageWidget->ordering+1 : 1;
                    
                    /** @var PageWidget */
                    $model->setIsNewRecord(true);
                    $model->id = null;
                    $model->template_content_id = 0;
                    $model->widget_settings_id = $wsId;
                    $model->page_id = $pageId;
                    $model->revision_id = $revision;
                    $model->ordering = $ordering;
                    if (!$model->save())
                    {
                        Yii::log(CVarDumper::dumpAsString($model->getErrors()), Clogger::LEVEL_ERROR);
                    }
                    else
                        $containers[$model->container][] = $model->id;
                }
            }
        }
        
        return $this->result->processed('containers', $containers);
    }
    
    
    /**
    * remove linked widgets of template with page
    * 
    * @param array $params
    *   page_id                 int     page id
    *   revision                int     page revision
    *   template_content_id     int     template content id
    *   
    * @return FServiceModel
    * 
    */
    public function unlinkWidgetsFromPage($params)
    {
        $containers = $this->getParam($params, 'containers', array(), 'xss, newline, tag');
        $pageId = $this->getParam($params, 'page_id', 0);
        $revision = $this->getParam($params, 'revision_id', 0);
        $tplContentId = $this->getParam($params, 'template_content_id', 0);
        
        $models = PageWidget::model()->findAllByAttributes(array('template_content_id'=>$tplContentId));
        if (is_array($models) && count($models))
        {
            $wsIds = CHtml::listData($models, 'id', 'id');
            $criteria = new CDbCriteria;
            $criteria->compare('page_id',$pageId);
            $criteria->compare('revision_id',$revision);
            $criteria->addInCondition('widget_settings_id', $wsIds);
            $pageWidgets = PageWidget::model()->findAll($criteria);
            if (is_array($pageWidgets) && count($pageWidgets))
            {
                foreach ($pageWidgets as $pageWidget)
                {
                    if (isset($containers[$pageWidget->container]) && is_array($containers[$pageWidget->container]) && count($containers[$pageWidget->container]))
                    {
                        $index = array_search($pageWidget->id, $containers[$pageWidget->container]);
                        if ($index !== false)
                            unset($containers[$pageWidget->container][$index]);
                    }
                    $pageWidget->delete();
                }
            }
        }
        
        return $this->result->processed('containers', $containers);
    }
    
    /**
    * find all parent recursive template of current template
    * 
    * @param mixed $params
    *   id  int template_content_id
    * 
    * @return array templates template_content      
    */
    public function getAllParent($params)
    {
        $id = $this->getParam($params, 'id', 0);
        
        $templates = array();
        
        $template = TemplateContent::model()->findByPk($id);
        while(is_object($template->parent))
        {
            $template = $template->parent;
            $templates[] = $template;
        }
        
        return $this->result->processed('templates', $templates);
    }
}